package com.engine.core.rowMapper;

import com.alibaba.fastjson.JSON;
import com.engine.core.util.Utils;
import org.apache.ibatis.cache.CacheKey;

import java.util.*;
import java.util.stream.Collectors;

public class StructBuildBiz {


    /**
     * 数据结构化方案
     * @param source
     * @param associationList
     * @return
     */
    public static List<Map<String,Object>> buildDataStruct(List<Map<String,Object>> source,List<Association> associationList){
        List<Map<String,Object>> mainDataList=new ArrayList<Map<String,Object>>();
        Optional<Association> rootAssociationOptional=associationList.stream().filter(s->s.isRoot).findFirst();
        List<Association> childrenAssociation=associationList.stream().sorted((s1,s2)->new Integer(s1.getKeyColumn().length).compareTo(new Integer(s2.keyColumn.length)))
                .filter(s1-> !s1.isRoot).collect(Collectors.toList());
        if(rootAssociationOptional.isPresent()){
              Association rootAssociation=rootAssociationOptional.get();
              String[] columnNames=rootAssociation.columnNames;
              String[] keyColumn=rootAssociation.keyColumn;
              Map<String,String> columnMapping=rootAssociation.columnMapping;
              Map<CacheKey,List<Map<String,Object>>> mainDataGroup=source.stream().collect(Collectors.groupingBy(s->getKeyValue(s,keyColumn)));
              Iterator<Map.Entry<CacheKey,List<Map<String,Object>>>> mainDataGroupIterator=mainDataGroup.entrySet().iterator();
              while(mainDataGroupIterator.hasNext()){
                  Map.Entry<CacheKey,List<Map<String,Object>>>  mainDataGroupEntry= mainDataGroupIterator.next();
                  List<Map<String,Object>> mainDataGroupEntryList=mainDataGroupEntry.getValue();
                  Map<String,Object> mainData=getValue(mainDataGroupEntryList.get(0),columnNames,columnMapping);
                  if(mainData!=null){
                      mainDataList.add(mainData);
                      setValue(mainDataGroupEntryList,mainData,childrenAssociation,true);
                  }
              }

        }
        return mainDataList;
    }




    private static CacheKey getKeyValue(Map<String,Object> source,String[] keyColumns){
         CacheKey cacheKey=new CacheKey();
         for (int i = 0; i < keyColumns.length; i++) {
              cacheKey.update(source.get(keyColumns[0]));
        }
        return cacheKey;
    }

    private static Map<String,Object> getValue(Map<String,Object> source,String[] columnNames,Map<String,String> columnMapping){
        Map<String,Object> values=new HashMap<>();
        if(columnMapping==null){
            columnMapping=new HashMap<>();
        }
        Set<String> columnNamesSet= new HashSet<>(columnMapping.keySet());
        columnNamesSet.addAll(Arrays.asList(columnNames));

        for (String columnName:columnNamesSet) {
            if(columnNamesSet.contains(columnName)){
                values.put(columnMapping.getOrDefault(columnName, columnName), source.get(columnName));
            }
        }
        if(values.size()>0){
            return values;
        }else{
            return null;
        }
    }

    public static void setValue(List<Map<String,Object>>  source,Map<String,Object> parent,List<Association> associationList,boolean isFirst){
        List<Association>  outerAssociationList=associationList.stream().filter(s->(s.propertyPath.length==1 && isFirst) || !isFirst).collect(Collectors.toList());
        for (int i = 0; i < outerAssociationList.size(); i++) {
            setLoopValue(source,parent,outerAssociationList.get(i),associationList);
        }
    }


    private static void setLoopValue(List<Map<String,Object>>  source,Map<String,Object> parent,Association current,List<Association> associationList){
        String[] columnNames=current.columnNames;
        String[] keyColumn=current.keyColumn;
        String[] namePath=current.propertyPath;
        Map<String,String> columnMapping=current.columnMapping;

        //分组计算当前对象以及其下级成员的信息
        Map<CacheKey,List<Map<String,Object>>> loopDataGroup=source.stream().collect(Collectors.groupingBy(s->getKeyValue(s,keyColumn)));
        Iterator<Map.Entry<CacheKey,List<Map<String,Object>>>> loopDataGroupIterator =loopDataGroup.entrySet().iterator();
        List<Map<String,Object>> childrenDataDest=new ArrayList<>();

        List<Association> childAssociationList =associationList.stream().filter(s->
                (Utils.wrap(String.join(",",s.propertyPath))).contains(Utils.wrap(String.join(",",current.propertyPath)))
                && !Utils.wrap(String.join(",",s.propertyPath)).equals(Utils.wrap(String.join(",",current.propertyPath)))
        ).collect(Collectors.toList());

        while(loopDataGroupIterator.hasNext()){
            Map.Entry<CacheKey,List<Map<String,Object>>> loopDataEntry=loopDataGroupIterator.next();
            List<Map<String,Object>> childrenDataSource=loopDataEntry.getValue();
            if(current.isArray){//如果下级成员是数组
                Map<String,Object> childDest=getValue(childrenDataSource.get(0),columnNames,columnMapping);
                if(childDest!=null){
                    childrenDataDest.add(childDest);
                    if(childAssociationList.size()>0){
                        for (int i = 0; i < childrenDataSource.size(); i++) {
                            setValue(childrenDataSource,childDest,childAssociationList,false);
                        }
                    }
                }
                parent.put(namePath[namePath.length-1],childrenDataDest);
            }else{//如果下级成员是obj
                Map<String,Object> childDest=getValue(childrenDataSource.get(0),columnNames,columnMapping);
                if(childDest!=null){
                    childrenDataDest.add(childDest);
                    if(childAssociationList.size()>0){
                        setValue(childrenDataSource,childDest,childAssociationList,false);
                    }
                }
                parent.put(namePath[namePath.length-1],childDest);
                break;
            }

        }
    }


    public static class Association{

        protected String[] propertyPath;

        private String[] columnNames;
        /**
         *   主键字段
         */
        protected String[] keyColumn;

        protected boolean isArray;

        protected boolean isRoot;

        protected Map<String,String>  columnMapping=new HashMap<>();

        private Association(String[] keyColumn,String[] columnNames,Map<String,String>  columnMapping) {
            this.isRoot=true;
            this.keyColumn=keyColumn;
            this.columnNames=columnNames;
            this.columnMapping=columnMapping;
        }

        private Association(String[] keyColumn,String[] columnNames) {
            this.isRoot=true;
            this.keyColumn=keyColumn;
            this.columnNames=columnNames;
        }

        public static Association root(String[] keyColumn,String[] columnNames){
            return new Association(keyColumn,columnNames);
        }

        public static Association root(String[] keyColumn,String[] columnNames,Map<String,String>  columnMapping){
            return new Association(keyColumn,columnNames,columnMapping);
        }

        public static Association array(String[] keyColumn,String[] columnNames,String[] propertyPath){
            return new Association(keyColumn,columnNames,propertyPath,true);
        }

        public static Association array(String[] keyColumn,String[] columnNames,String[] propertyPath,Map<String,String>  columnMapping){
            return new Association(keyColumn,columnNames,propertyPath,columnMapping,true);
        }

        public static Association object(String[] keyColumn,String[] columnNames,String[] propertyPath){
            return new Association(keyColumn,columnNames,propertyPath,false);
        }
        public static Association object(String[] keyColumn,String[] columnNames,String[] propertyPath,Map<String,String>  columnMapping){
            return new Association(keyColumn,columnNames,propertyPath,columnMapping,false);
        }

        public Association(String[] keyColumn, String[] columnNames,String[] propertyPath ,boolean isArray) {
            this.columnNames = columnNames;
            this.keyColumn = keyColumn;
            this.propertyPath=propertyPath;
            this.isArray=isArray;
        }

        public Association(String[] keyColumn, String[] columnNames,String[] propertyPath ,Map<String,String>  columnMapping,boolean isArray) {
            this.columnNames = columnNames;
            this.keyColumn = keyColumn;
            this.propertyPath=propertyPath;
            this.isArray=isArray;
            this.columnMapping=columnMapping;
        }


        public String[] getKeyColumn() {
            return keyColumn;
        }

        public void setKeyColumn(String[] keyColumn) {
            this.keyColumn = keyColumn;
        }

        public String[] getColumnNames() {
            return columnNames;
        }

        public void setColumnNames(String[] columnNames) {
            this.columnNames = columnNames;
        }

        public String[] getPropertyPath() {
            return propertyPath;
        }

        public void setPropertyPath(String[] propertyPath) {
            this.propertyPath = propertyPath;
        }

        public boolean isArray() {
            return isArray;
        }

        public void setArray(boolean array) {
            isArray = array;
        }

        public boolean isRoot() {
            return isRoot;
        }

        public void setRoot(boolean root) {
            isRoot = root;
        }

        public Map<String, String> getColumnMapping() {
            return columnMapping;
        }

        public void setColumnMapping(Map<String, String> columnMapping) {
            this.columnMapping = columnMapping;
        }
    }

    class TestBean{
        String[] names;

        public String[] getNames() {
            return names;
        }

        public void setNames(String[] names) {
            this.names = names;
        }
    }

    public static void main(String[] args) {
         List<Association> associationList=new ArrayList<>();
         Map<String,String> akeyMap=new HashMap<>();
         akeyMap.put("aiKey","key");
        akeyMap.put("aiVal","value");

         associationList.add(Association.root(new String[]{"code"},new String[]{"code","name","age","birthday"}));
         associationList.add(Association.object(new String[]{"aid"},new String[]{"aid","provnice","city"},new String[]{"address"}));
         associationList.add(Association.array(new String[]{"aiKey"},new String[]{"aiKey","aiVal"},new String[]{"aihao"},akeyMap));
        associationList.add(Association.object(new String[]{"workcode"},new String[]{"workcode","workName"},new String[]{"work"}));
        associationList.add(Association.array(new String[]{"workaddresscode"},new String[]{"workaddresscode","workaddressName"},new String[]{"work","workaddress"}));

        System.out.println(JSON.toJSONString(associationList));
//        associationList.sort((s1,s2)->new Integer(s1.getKeyColumn().length).compareTo(new Integer(s2.keyColumn.length)));

        List<Map<String,Object>> mainData=new ArrayList<>();

        Map<String,Object> o1=new HashMap<>();
        o1.put("code","1001");
        o1.put("name","wangjun");
        o1.put("age","36");
        o1.put("birthday","1986-0919");

        o1.put("aid","030031");
        o1.put("provnice","山西");
        o1.put("city","太远");

        o1.put("aiKey","01");
        o1.put("aiVal","吃饭");

        o1.put("workcode","1001");
        o1.put("workName","出差");

        o1.put("workaddresscode","10003");
        o1.put("workaddressName","山西吕梁");

        mainData.add(o1);


        Map<String,Object> o2=new HashMap<>();
        o2.put("code","1001");
        o2.put("name","wangjun");
        o2.put("age","36");
        o2.put("birthday","1986-0919");
        o2.put("aid","030031");
        o2.put("provnice","山西");
        o2.put("city","太远");
        o2.put("aiKey","02");
        o2.put("aiVal","睡觉");

        o2.put("workcode","1001");
        o2.put("workName","出差");

        o2.put("workaddresscode","10001");
        o2.put("workaddressName","山西太原");
        mainData.add(o2);


        Map<String,Object> o3=new HashMap<>();
        o3.put("code","1001");
        o3.put("name","wangjun");
        o3.put("age","36");
        o3.put("birthday","1986-0919");
        o3.put("aid","030032");
        o3.put("provnice","山西");
        o3.put("city","太远");
        o3.put("aiKey","03");
        o3.put("aiVal","XXX");

        o3.put("workcode","1001");
        o3.put("workName","出差");

        o3.put("workaddresscode","10002");
        o3.put("workaddressName","山西忻州");
        mainData.add(o3);

      List<Map<String,Object>> allDatas=buildDataStruct(mainData,associationList);

        System.out.println(JSON.toJSONString(allDatas));

        String jsons="{" +
                "\"names\":[\"123\",\"456\"]" +
                "}";
        System.out.println(JSON.isValidObject(jsons));

        TestBean testBean= JSON.parseObject(jsons,TestBean.class);

        System.out.println(testBean.getNames()[0]);


    }
}
